package com.asia5b.a5mall.middleware.api

import android.app.Application
import android.content.Context
import android.text.TextUtils
import android.util.Log
import com.asia5b.a5bmall.resource.ResString
import com.asia5b.a5mall.middleware.R
import com.asia5b.a5mall.middleware.config.GlobalValue
import com.asia5b.a5mall.middleware.config.UserRepository
import com.asia5b.a5mall.middleware.event.LogoutEvent
import com.asia5b.a5mall.middleware.gsonadapter.ResponseConvertFactory
import com.asia5b.a5mall.middleware.utils.*
import com.asia5b.a5mall.middleware.utils.cache.ACache
import com.franmontiel.persistentcookiejar.PersistentCookieJar
import com.franmontiel.persistentcookiejar.cache.SetCookieCache
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import okhttp3.*
import okhttp3.logging.HttpLoggingInterceptor
import okio.Buffer
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import timber.log.Timber
import java.io.File
import java.io.IOException
import java.nio.charset.Charset
import java.util.concurrent.TimeUnit


/**
 * Created by zhangyuncai on 2019/12/21.
 * @Note   网络工厂类
 */
class NetFectory {

    companion object {
        val LINE_SEPARATOR = System.getProperty("line.separator")
        fun build(context: Application): NetFectory {
            return NetFectory(context)
        }


        fun printJson(msg: String) {

            var message: String

            try {
                if (msg.startsWith("{")) {
                    val jsonObject = JSONObject(msg)
                    message = jsonObject.toString(4)//最重要的方法，就一行，返回格式化的json字符串，其中的数字4是缩进字符数
                } else if (msg.startsWith("[")) {
                    val jsonArray = JSONArray(msg)
                    message = jsonArray.toString(4)
                } else {
                    message = msg
                }
            } catch (e: JSONException) {
                message = msg
            }

            Timber.d("╔═══════════════════════════════════════════════════════════════════════════════════════")
            message = LINE_SEPARATOR + message
            val lines = message.split(LINE_SEPARATOR.toRegex()).dropLastWhile({ it.isEmpty() }).toTypedArray()
            for (line in lines) {
                //            LogUtil.d("", line);
                Timber.d(line)
            }
            Timber.d("╚═══════════════════════════════════════════════════════════════════════════════════════")
        }

    }

    /**
     * @Name  DEFAULT_TIMEOUT
     * @Type  Long
     * @Note  默认超时
     */
    private val DEFAULT_TIMEOUT: Long = 15

    private val context: Application

    /**
     * 构造
     */
    private constructor(context: Application) {
        this.context = context
        this.okHttpClient = OkHttpClient.Builder()
                .cookieJar(
                        PersistentCookieJar(SetCookieCache()
                                , SharedPrefsCookiePersistor(context)))
                .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .addInterceptor(getLog())
                .addInterceptor(HeadParamsInterceptor())
                .addInterceptor(TokenInterceptor(context))
                .cache(Cache(File(context.cacheDir, "NetCache"),
                        (10 * 1024 * 1024).toLong())).build()
    }

    /**
     * OkHttp客户端
     */
    private val okHttpClient: OkHttpClient

    /**
     * GSON 格式化对象,决定序列化和反序列化 java.util.Date，java.sql.Timestamp，java.sql.Timestamp的格式
     */
    private val mGsonDateFormat: Gson = GsonBuilder().setDateFormat("yyyy-MM-dd hh:mm:ss").create()

    /**
     * 配置Logger
     * @return
     */
    public fun getLog(): HttpLoggingInterceptor {
        val logging = HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
            override fun log(message: String) {
                if (!TextUtils.isEmpty(message)) {
                    val s = message.substring(0, 1)
                    //如果收到响应是json才打印
                    if ("{" == s || "[" == s) {
                        printJson(message)
                    } else {
                        Log.e("PayFectory", message)
                    }
                }
            }

        })
        logging.level = if (GlobalValue.isDebug) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
        return logging
    }


    /**
     * @Note   创建Api服务
     * @param  serviceClass apiClass
     * @param  url 基础URL
     */
    fun <S> createService(serviceClass: Class<S>, url: String): S {

        //构建服务
        return Retrofit.Builder()
                //配置基础URLs
                .baseUrl(url)
                //配置网络客服端
                .client(okHttpClient)
                //添加Gson转换，json直接转换为相应格式的对象
//                .addConverterFactory(GsonConverterFactory.create(mGsonDateFormat))
                .addConverterFactory(ResponseConvertFactory.create(context, mGsonDateFormat))
                //添加Rxjava回调语法
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                //构建
                .build().create(serviceClass)
    }

}


/**
 * 请求头参数管理
 */
class HeadParamsInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
                .newBuilder()
                .addHeader("countryId", "$appCountrySelect")//国家公馆
                .build()
        return chain.proceed(request)
    }

}

/**
 * @Note   token全局刷新器
 */
class TokenInterceptor : Interceptor {

    /**
     * token管理器
     */
    private val tokenManager: TokenManager

    /**
     * 构造方法
     */
    constructor(context: Context) {
        tokenManager = TokenManager(context)
    }

    /**
     * @Note   开始拦截
     * @param  chain   数据
     */
    override fun intercept(chain: Interceptor.Chain): Response {

        /**保存原始Request*/
        var originalRequest = chain.request()

        /**保存builder*/
        val builder = originalRequest.newBuilder()

        /**设置token*/
        initToken(builder, originalRequest)

        /**全局添加uuid*/
        originalRequest = builder.addHeader("uuid", UUID.uuid)
                .build()

        /**获取请求相应体*/
        val response = chain.proceed(originalRequest)

        /**判断Token是否失效  如果失效 进入该方法 刷新token*/
        if (checkToken(response)) {

            /**拿到刷新Token*/
            var refreshToken = tokenManager.getRefreshToken()

            /**判断是否持有刷新token 不持有的话 抛出未登录异常*/
            if (refreshToken == null) {
                UserRepository.setUserInfo(null)
                /**发送退出登录广播*/
                getEventBus().post(LogoutEvent())
                showMessage(R.string.登录失效_请重新登录)
                throw CustomThrowable(ResString.get(R.string.登录失效_请重新登录)).apply { this.isToast = true }
            }

            /**刷新token值*/
            val result = ApiManager.refreshToken(refreshToken)

            /**刷新token失效 清空数据 抛异常*/
            if (result.code != 200) {
                tokenManager.clearToken()
                tokenManager.removeUid()
                UserRepository.setUserInfo(null)
                /**发送退出登录广播*/
                getEventBus().post(LogoutEvent())
                showMessage(R.string.登录失效_请重新登录)
                throw CustomThrowable(ResString.get(R.string.登录失效_请重新登录)).apply { this.isToast = true }
            }

            /** 获取到新的token  初始化JsonObject*/
            val jsonResult = JSONObject(result.body!!.getJsonString())

            /** 拿到权限token */
            val accessToken = jsonResult.getString("accessToken")

            /** 拿到刷新token */
            refreshToken = jsonResult.getString("refreshToken")

            /** 刷新token */
            tokenManager.updateToken(refreshToken, accessToken)

            var requestBody: RequestBody? = null

            if (originalRequest.body is FormBody) {
                /**初始化新的formbody*/
                val newBuilder = FormBody.Builder()

                /**判断原始request的body是否为空 不为空的情况下 将原始请求的body数据 循环设置到新body中*/
                if (originalRequest.body != null) {
                    var originalFormBody = originalRequest.body as FormBody
                    for (i in 0 until originalFormBody.size) {
                        newBuilder.add(originalFormBody.name(i), originalFormBody.value(i))
                    }
                }

                /** 初始化body */
                requestBody = newBuilder.add("clintType", "android").build()
            } else if (originalRequest.body is RequestBody) {
                val jsonObject: JSONObject
                if (originalRequest.body!!.contentLength() == 0L) {
                    jsonObject = JSONObject()
                } else {
                    jsonObject = JSONObject(getParamContent(originalRequest.body!!))
                }
                requestBody = RequestBody.create(originalRequest.body!!.contentType(), jsonObject.toString())
            }


            /** 构建Request */
            val newRequest = originalRequest.newBuilder()
                    .method(originalRequest.method, (originalRequest.body == null).judge(null, requestBody))

            /**设置token*/
            initToken(newRequest, originalRequest)

            /** 放入拦截链路，重新请求 */
            return chain.proceed(newRequest.build())

        } else {

            /**如果进入该方法 代表token未失效*/

            if (originalRequest.url.encodedPath.contains("members/logout") && response.code == 200) {
                tokenManager.removeUid()
                tokenManager.clearToken()
                /**如果该次请求是登录请求 则在登录请求中获取 token和uid 存储或更新到本地*/
            } else if ((originalRequest.url.encodedPath.contains("passport/connect/app")
                            || originalRequest.url.encodedPath.contains("passport/sms-binder/app")
                            || originalRequest.url.encodedPath.contains("passport/login-binder/app")
                            || originalRequest.url.encodedPath.contains("/passport/login")
                            || originalRequest.url.encodedPath.contains("passport/register/wap"))
                    && response.code == 200) {

                /**获取Json字符串*/
                val source = response.body!!.source()
                source.request(Long.MAX_VALUE)
                val buffer = source.buffer()
                val result = buffer.clone().readString(Charset.forName("UTF-8"))

                /**当json不为空 获取uid与token 并更新*/
                if (result != null &&
                        result != "" &&
                        result.contains("access_token") &&
                        result.contains("uid") &&
                        result.contains("refresh_token")) {

                    /**初始化JSON OBJECT*/
                    val jsonResult = JSONObject(result)

                    if (jsonResult.has("uid") && jsonResult.has("access_token") && jsonResult.has("refresh_token")) {
                        /**获取uid*/
                        val uid = jsonResult.getInt("uid")

                        /**获取权限Token*/
                        val accessToken = jsonResult.getString("access_token")

                        /**获取刷新token*/
                        val refreshToken = jsonResult.getString("refresh_token")

                        /**刷新token*/
                        tokenManager.updateToken(refreshToken, accessToken)

                        /**刷新uid*/
                        tokenManager.updateUid(uid)
                    }

                }
            }

            return response

        }
    }

    /**
     * @Note   检查token是否失效
     * @param  response 相应体
     */
    private fun checkToken(response: Response): Boolean {

        if (response.code == 401 || response.code == 403) {
            return true
        }

        return false
    }

    /**
     * 获取常规post请求参数
     */
    @Throws(IOException::class)
    private fun getParamContent(body: RequestBody): String {
        val buffer = Buffer()
        body.writeTo(buffer)
        return buffer.readUtf8()
    }

    /**
     * @Note   设置请求体中的token参数
     * @param  builder 请求体构建者
     * @param  originalRequest 原始请求体
     */
    private fun initToken(builder: Request.Builder, originalRequest: Request) {

        /**拿到权限token*/
        val accessToken = tokenManager.getAccessToken()

        /**拿到uid*/
        val uid = tokenManager.getUid()

        /**开发环境时  只设置权限token*/
        //todo 之前的代码是开发环境才传Authorization,后来朱工让正式/测试都传
        if (accessToken != null) {
            builder.removeHeader("Authorization")
            builder.addHeader("Authorization", accessToken)
        }
        /**判断APP 环境*/
        if (GlobalValue.appType == 0) {


        } else {

            /** 生产环境时 设置 随机字符串 时间戳 签名 uid */
            if (accessToken != null || uid != null) {

                val nonce = ((Math.random() * 9 + 1) * 100000).toInt().toString()

                val timestamp = (System.currentTimeMillis() / 1000).toString()

                val sign = MD5Util.MD5Encode("$uid$nonce$timestamp$accessToken", null)

                val orgUrl = originalRequest.url

                builder.url(orgUrl.newBuilder()
                        .removeAllQueryParameters("uid")
                        .removeAllQueryParameters("nonce")
                        .removeAllQueryParameters("timestamp")
                        .removeAllQueryParameters("sign")
                        .addQueryParameter("uid", uid)
                        .addQueryParameter("nonce", nonce)
                        .addQueryParameter("timestamp", timestamp)
                        /*.addQueryParameter("sign", sign)*/.build()//todo 记得将这段注释移除掉
                )
            }
        }
    }

}


/**
 * @Note   token管理器
 */
class TokenManager {

    /**
     * @Name  context
     * @Type  Context
     * @Note  上下文
     */
    private val context: Context

    /**
     * @Note   构造方法
     * @param  context 上下文
     */
    constructor(context: Context) {
        this.context = context
    }

    /**
     * 刷新TokenKey
     */
    private val REFRESH_TOKEN_KEY = "REFRESH_TOKEN_KEY"

    /**
     * 权限TokenKey
     */
    private val ACCESS_TOKEN_KEY = "ACCESS_TOKEN_KEY"

    /**
     * UID Key
     */
    private val UID_KEY = "UID_KEY"

    /**
     * @Note   获取刷新Token
     */
    fun getRefreshToken(): String? {
        return ACache.get(context).getAsString(REFRESH_TOKEN_KEY)
    }

    /**
     * @Note   获取权限Token
     */
    fun getAccessToken(): String? {
        return ACache.get(context).getAsString(ACCESS_TOKEN_KEY)
    }

    /**
     * 获取UID
     */
    fun getUid(): String? {
        return ACache.get(context).getAsString(UID_KEY)
    }

    /**
     * 移除UID
     */
    fun removeUid() {
        ACache.get(context).remove(UID_KEY)
    }

    /**
     * 更新UID
     */
    fun updateUid(uid: Int) {
        ACache.get(context).put(UID_KEY, uid.toString())
    }

    /**
     * @Note   清空Token
     */
    fun clearToken() {
        ACache.get(context).remove(REFRESH_TOKEN_KEY)
        ACache.get(context).remove(ACCESS_TOKEN_KEY)
    }

    /**
     * @Note   更新Token
     * @param  refreshToken 刷新Token
     * @param  accessToken  权限Token
     */
    fun updateToken(refreshToken: String, accessToken: String) {
        ACache.get(context).put(REFRESH_TOKEN_KEY, refreshToken)
        ACache.get(context).put(ACCESS_TOKEN_KEY, accessToken)
    }

}


